//
//  EUPageJump.h
//
//  Created by WuJianNan on 11/20/14.
//  Copyright (c) 2014 nd.com.cn. All rights reserved.
//

#import "EUPageJump.h"
#import "EUPageJumpViewController.h"
#import <objc/runtime.h>
#import <objc/message.h>

@interface EUPageJump ()
@property(nonatomic, strong) NSString *lastestCallBackID;
@end

@implementation EUPageJump

+ (UIViewController *)getInstanceVC:(NSString *)controllerName {
    if (controllerName && controllerName.length > 0) {
        UIViewController *vc = [NSClassFromString(controllerName) new];
        return vc;
    }
    return nil;
}

/**
 *  @author liubiqu@qq.com, 15-08-18 14:08:16
 *
 *  处理第一个字符转为大写
 *
 *  @param str <#str description#>
 *
 *  @return <#return value description#>
 */
+ (NSString *)ConvertFirstCharUpperCase:(NSString *)str {
    NSString *strResult = [str copy];
    if (str && str.length > 0) {
        strResult = [NSString stringWithFormat:@"%@%@", [[strResult substringToIndex:1] uppercaseString], [strResult substringFromIndex:1]];
    }
    return strResult;
}

//+ (NSMutableArray *)getProperties:(Class) class {
//    NSMutableArray *properties = [NSMutableArray new];
//    unsigned int outCount, i;
//    objc_property_t *objc_properties = class_copyPropertyList(class, &outCount);
//    for (i = 0; i < outCount; i++) {
//        objc_objectptr_t property = objc_properties[i];
//        const char *propName = property_getName(property);
//        const char *propAttr = property_getAttributes(property);
//        if (propAttr) {
//            NSLog(@"propAttr %@", [NSString stringWithUTF8String:propAttr]);
//        }
//        if (propName) {
//            // NSString *propertyName = [NSString stringWithCString:propName encoding:[NSString defaultCStringEncoding]];
//            NSString *propertyName = [NSString stringWithUTF8String:propName];
//            NSLog(@"propertyName %@", propertyName);
//            NSString *strName = [NSString stringWithFormat:@"_%@", propertyName];
//            Ivar ivar = class_getInstanceVariable(class, [strName cString]);
//            char *ivartype = ivar_getTypeEncoding(ivar);
//            NSLog(@"ivartype:%s", ivartype);
//            [properties addObject:propertyName];
//        }
//    }
//    free(objc_properties);
//    return properties;
//}

#pragma mark - nav jump methods
- (void)pushPage:(CDVInvokedUrlCommand *)command {
    //__weak EUPageJump *weakSelf = self;
    [self.commandDelegate runInBackground:^{
        self.lastestCallBackID = nil;
        NSArray *arguments = command.arguments;
        if (arguments && arguments.count >= 2) {
            if (self.viewController.navigationController) {
                NSDictionary *jumpControlData = [self embedDataWithPageID:command dataIndex:1];
                NSDictionary *fromData = [self embedDataWithPageID:command dataIndex:0];
                NSString *targetPagePath = [jumpControlData valueForKey:kControlTargetPagePathKey];
                if (!targetPagePath) {
                    targetPagePath = @"index.html";
                }
                dispatch_async(dispatch_get_main_queue(), ^{
                    UIViewController *nativeVC = [EUPageJump getInstanceVC:[jumpControlData objectForKey:kControlControllerName]];
                    if (nativeVC) {
                        if ([nativeVC isKindOfClass:[EUPageJumpViewController class]]) {
                            EUPageJumpViewController *jumpVC = (EUPageJumpViewController *)nativeVC;
                            jumpVC.fromData = fromData;
                            jumpVC.jumpControlData = jumpControlData;
                            jumpVC.startPage = targetPagePath;
                        }
                        //[EUPageJump getProperties:[nativeVC class]];
                        [fromData enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
                            NSString *strKey = [EUPageJump ConvertFirstCharUpperCase:key];
                            SEL sel = NSSelectorFromString([NSString stringWithFormat:@"set%@:", strKey]);
                            if ([nativeVC respondsToSelector:sel]) {
                                //获取变量的类型进行判断赋值，如果不是nsobject子类需要转换后设值，否则无法成功赋值
                                NSString *strName = [NSString stringWithFormat:@"_%@", key];
                                Ivar ivar = class_getInstanceVariable([nativeVC class], [strName UTF8String]);
                                const char *ivarType = ivar_getTypeEncoding(ivar);
                                NSLogInfo(@"找到属性:%@|%@|%s", key, strKey, ivarType);
                                if (ivarType[0] == '@') {
                                    objc_msgSend(nativeVC, sel, obj);
                                } else {
                                    if (ivarType[0] == 'q' || ivarType[0] == 'Q') {
                                        objc_msgSend(nativeVC, sel, [obj longLongValue]);
                                    } else if (ivarType[0] == 'i' || ivarType[0] == 'I') {
                                        objc_msgSend(nativeVC, sel, [obj intValue]);
                                    } else if (ivarType[0] == 'd' || ivarType[0] == 'D') {
                                        objc_msgSend(nativeVC, sel, [obj doubleValue]);
                                    } else if (ivarType[0] == 'f' || ivarType[0] == 'F') {
                                        objc_msgSend(nativeVC, sel, [obj floatValue]);
                                    } else if (ivarType[0] == 'b' || ivarType[0] == 'B') {
                                        objc_msgSend(nativeVC, sel, [obj boolValue]);
                                    } else {
                                        objc_msgSend(nativeVC, sel, [obj intValue]);
                                    }
                                }
                            } else {
                                NSLogWarn(@"未找到对应的属性:%@|%@", key, strKey);
                            }
                        }];

                        [self.viewController.navigationController pushViewController:nativeVC animated:YES];
                    } else {
                        EUPageJumpViewController *vc = [[EUPageJumpViewController alloc] init];
                        vc.fromData = fromData;
                        vc.jumpControlData = jumpControlData;
                        vc.startPage = targetPagePath;
                        [self.viewController.navigationController pushViewController:vc animated:YES];
                    }
                });

                NSString *resultMsg = @"EUPageJump pushPage: push success";
                CDVPluginResult *result =
                    [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:@{
                        @"onStart" : @(1),
                        @"data" : @{@"resultMsg" : resultMsg}
                    }];
                result.keepCallback = @(1);
                [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
                self.lastestCallBackID = command.callbackId;
            } else {
                [self sendNotEmbedInPushStackError:command];
            }
        } else {
            NSString *errorMsg = @"EUPageJump pushPage: 至少两个参数（reuqire arguments）";
            CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:errorMsg];
            [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
        }
    }];
}

- (void)popPage:(CDVInvokedUrlCommand *)command {
    if (self.viewController.navigationController) {
        if ([self currentPageIndex] > 0) {
            EUPageJumpViewController *targetVC = [self getPrePageInPushStack];
            if (targetVC) {
                targetVC.resumeData = [self embedDataWithPageID:command dataIndex:0];
                [targetVC onNextPageClose];
                if ([command.arguments count] > 0) {
                    BOOL isRefresh = (BOOL)command.arguments[0];
                    if (isRefresh) {
                        [targetVC reloadWebView];
                    }
                }
            }

            [self.viewController.navigationController popViewControllerAnimated:YES];
            NSString *resultMsg = @"EUPageJump popPage: succsee";
            CDVCommandStatus status = CDVCommandStatus_OK;
            CDVPluginResult *result = [CDVPluginResult resultWithStatus:status messageAsString:resultMsg];
            [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
        } else {
            [self sendUnableToPopRootPageError:command];
        }
    } else {
        [self sendNotEmbedInPushStackError:command];
    }
}

- (void)popToRoot:(CDVInvokedUrlCommand *)command {
    if (self.viewController.navigationController) {
        if ([self currentPageIndex] > 0) {
            EUPageJumpViewController *targetVC = [self getRootPageInPushStack];
            if (targetVC) {
                targetVC.resumeData = [self embedDataWithPageID:command dataIndex:0];
                [targetVC onNextPageClose];
            }
            [self.viewController.navigationController popToRootViewControllerAnimated:YES];
            NSString *resultMsg = @"EUPageJump popToRoot: succsee";
            CDVCommandStatus status = CDVCommandStatus_OK;
            CDVPluginResult *result = [CDVPluginResult resultWithStatus:status messageAsString:resultMsg];
            [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
        } else {
            [self sendUnableToPopRootPageError:command];
        }
    } else {
        [self sendNotEmbedInPushStackError:command];
    }
}

- (void)popToPageWithPageID:(CDVInvokedUrlCommand *)command {
    if (self.viewController.navigationController) {
        if ([self currentPageIndex] > 0) {
            NSDictionary *controlDate = [self embedDataWithPageID:command dataIndex:1];
            NSString *targetPageID = [controlDate valueForKey:kControlTargetPageIDKey];
            if (targetPageID) {
                EUPageJumpViewController *targetVC = [self getPageWithPageIDInPushStack:targetPageID];
                if (targetVC) {
                    targetVC.resumeData = [self embedDataWithPageID:command dataIndex:0];
                    [targetVC onNextPageClose];
                }
                [self.viewController.navigationController popToViewController:targetVC animated:YES];
                NSString *resultMsg = @"EUPageJump popToPageWithPageID: succsee";
                CDVCommandStatus status = CDVCommandStatus_OK;
                CDVPluginResult *result = [CDVPluginResult resultWithStatus:status messageAsString:resultMsg];
                [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
            } else {
                [self sendPageNotFoundByPageIDError:command];
            }
        } else {
            [self sendUnableToPopRootPageError:command];
        }
    } else {
        [self sendNotEmbedInPushStackError:command];
    }
}

#pragma mark - mode jump methods
- (void)presentModelPage:(CDVInvokedUrlCommand *)command {
    self.lastestCallBackID = nil;
    NSArray *arguments = command.arguments;
    if (arguments && arguments.count >= 2) {

        NSDictionary *jumpControlData = [self embedDataWithPageID:command dataIndex:1];
        NSDictionary *fromData = [self embedDataWithPageID:command dataIndex:0];
        NSString *targetPagePath = [jumpControlData valueForKey:kControlTargetPagePathKey];
        if (!targetPagePath) {
            targetPagePath = @"index.html";
        }
        EUPageJumpViewController *vc = [[EUPageJumpViewController alloc] init];
        vc.fromData = fromData;
        vc.jumpControlData = jumpControlData;
        vc.startPage = targetPagePath;
        UIModalTransitionStyle transitionSytle = UIModalTransitionStyleCoverVertical;

        if ([jumpControlData valueForKey:kControlTransitionModelAnimTypeKey]) {
            NSInteger animType = [[jumpControlData valueForKey:kControlTransitionModelAnimTypeKey] integerValue];
            switch (animType) {
            case CoverVertical: {
                transitionSytle = UIModalTransitionStyleCoverVertical;
            } break;
            case FlipHorizontal: {
                transitionSytle = UIModalTransitionStyleFlipHorizontal;
            } break;
            case CrossDissolve: {
                transitionSytle = UIModalTransitionStyleCrossDissolve;
            } break;
            case PartialCurl: {
                transitionSytle = UIModalTransitionStylePartialCurl;
            } break;

            default:
                break;
            }
        }
        [self.viewController setModalTransitionStyle:transitionSytle];
        [self.viewController presentViewController:vc animated:YES completion:nil];
        NSString *resultMsg = @"EUPageJump presentModelPage: present success";
        CDVCommandStatus status = CDVCommandStatus_OK;
        CDVPluginResult *result = [CDVPluginResult resultWithStatus:status messageAsDictionary:@{ @"onStart" : @(1), @"data" : @{@"resultMsg" : resultMsg} }];
        result.keepCallback = @(1);
        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
        self.lastestCallBackID = command.callbackId;
    } else {
        NSString *errorMsg = @"EUPageJump presentModelPage: miss reuqire arguments";
        CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:errorMsg];
        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
    }
}

- (void)dismissModelPage:(CDVInvokedUrlCommand *)command {
    if (self.viewController.presentingViewController) {
        if ([self.viewController.presentingViewController isKindOfClass:[EUPageJumpViewController class]]) {
            ((EUPageJumpViewController *)self.viewController.presentingViewController).resumeData = [self embedDataWithPageID:command dataIndex:0];
        } else if ([self.viewController.presentingViewController isKindOfClass:[UINavigationController class]]) {
            NSArray *stackedVCs = [((UINavigationController *)self.viewController.presentingViewController)viewControllers];
            if (stackedVCs && stackedVCs.count > 0) {
                UIViewController *targetVC = [stackedVCs lastObject];
                if ([targetVC isKindOfClass:[EUPageJumpViewController class]]) {
                    ((EUPageJumpViewController *)targetVC).resumeData = [self embedDataWithPageID:command dataIndex:0];
                    [((EUPageJumpViewController *)targetVC)onNextPageClose];
                }
            }
        }
        [self.viewController dismissViewControllerAnimated:YES completion:nil];
        NSString *resultMsg = @"EUPageJump dismissModelPage: diss success";
        CDVCommandStatus status = CDVCommandStatus_OK;
        CDVPluginResult *result = [CDVPluginResult resultWithStatus:status messageAsString:resultMsg];
        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
    } else {
        [self sendUnableToDismissPageError:command];
    }
}

#pragma mark - refresh methods

- (void)refreshPage:(CDVInvokedUrlCommand *)command {
    if ([self.viewController isKindOfClass:[EUPageJumpViewController class]]) {
        NSDictionary *fromData = [self embedDataWithPageID:command dataIndex:0];
        EUPageJumpViewController *selfVC = (EUPageJumpViewController *)self.viewController;
        selfVC.fromData = fromData;
        NSString *resultMsg = @"EUPageJump refreshPage: start refresh";
        CDVCommandStatus status = CDVCommandStatus_OK;
        CDVPluginResult *result = [CDVPluginResult resultWithStatus:status messageAsString:resultMsg];
        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
        [selfVC.webView reload];
    } else {
        [self sendRefreshPageError:command];
    }
}

#pragma mark - util methods

- (void)onNextPageClose {
    if (self.lastestCallBackID) {
        NSDictionary *resumeData = [self getResumeDataFormVC];
        CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:@{ @"onStart" : @(0), @"data" : resumeData }];
        [self.commandDelegate sendPluginResult:result callbackId:self.lastestCallBackID];
    }
}

- (void)getFromData:(CDVInvokedUrlCommand *)command {
    NSDictionary *fromData = nil;
    if ([self.viewController isKindOfClass:[EUPageJumpViewController class]]) {
        if (((EUPageJumpViewController *)self.viewController).fromData) {
            fromData = [((EUPageJumpViewController *)self.viewController).fromData copy];
        }
        // formData被取过一次之后，就会被置空
        ((EUPageJumpViewController *)self.viewController).fromData = @{};
    }
    if (!fromData) {
        fromData = @{};
    }
    CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fromData];
    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}

- (void)getResumeData:(CDVInvokedUrlCommand *)command {
    NSDictionary *resumeData = [self getResumeDataFormVC];
    CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:resumeData];
    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}

- (NSDictionary *)getResumeDataFormVC {
    NSDictionary *resumeData = nil;
    if ([self.viewController isKindOfClass:[EUPageJumpViewController class]]) {
        if (((EUPageJumpViewController *)self.viewController).resumeData) {
            resumeData = [((EUPageJumpViewController *)self.viewController).resumeData copy];
        }
        // resumeData被取过一次之后，就会被置空
        ((EUPageJumpViewController *)self.viewController).resumeData = @{};
    }
    if (!resumeData) {
        resumeData = @{};
    }
    return resumeData;
}

- (void)getCurrentPageID:(CDVInvokedUrlCommand *)command {
    NSString *pageID = [self currentPageID];
    CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:pageID];
    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}

- (void)getCurrentPageIndex:(CDVInvokedUrlCommand *)command {
    NSInteger currentPageIndex = [self currentPageIndex];
    CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:(int)currentPageIndex];
    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}

#pragma mark - private methods

- (void)sendPageNotFoundByPageIDError:(CDVInvokedUrlCommand *)command {
    NSString *errorMsg = @"EUPageJump: unable to find page by pageID in push stack";
    CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:errorMsg];
    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}

- (void)sendUnableToPopRootPageError:(CDVInvokedUrlCommand *)command {
    NSString *errorMsg = @"EUPageJump: unable to pop root page";
    CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:errorMsg];
    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}

- (void)sendNotEmbedInPushStackError:(CDVInvokedUrlCommand *)command {
    NSString *errorMsg = @"EUPageJump: page isn't embed in push stack";
    CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:errorMsg];
    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}

- (void)sendUnableToDismissPageError:(CDVInvokedUrlCommand *)command {
    NSString *errorMsg = @"EUPageJump: unable to dismiss page";
    CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:errorMsg];
    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}

- (void)sendRefreshPageError:(CDVInvokedUrlCommand *)command {
    NSString *errorMsg = @"EUPageJump: unable to refresh page";
    CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:errorMsg];
    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}

- (NSInteger)currentPageIndex {
    NSInteger currentPageIndex = 0;
    if (self.viewController && self.viewController.navigationController) {
        NSArray *viewControllers = self.viewController.navigationController.viewControllers;
        for (NSInteger pageIndex = viewControllers.count - 1; pageIndex > 0; pageIndex--) {
            if (self.viewController == [viewControllers objectAtIndex:pageIndex]) {
                currentPageIndex = pageIndex;
                break;
            }
        }
    }
    return currentPageIndex;
}

- (NSString *)currentPageID {
    return [NSString stringWithFormat:@"%lld", (long long)self.viewController];
}

- (EUPageJumpViewController *)getPrePageInPushStack {
    EUPageJumpViewController *targetVC = nil;
    if (self.viewController.navigationController) {
        NSArray *stackedVCs = self.viewController.navigationController.viewControllers;
        NSInteger currentPageIndex = [self currentPageIndex];
        if (stackedVCs && stackedVCs.count > 1 && currentPageIndex >= 1) {
            UIViewController *previouseVC = [stackedVCs objectAtIndex:currentPageIndex - 1];
            if ([previouseVC isKindOfClass:[EUPageJumpViewController class]]) {
                targetVC = (EUPageJumpViewController *)previouseVC;
            }
        }
    }
    return targetVC;
}

- (EUPageJumpViewController *)getRootPageInPushStack {
    EUPageJumpViewController *targetVC = nil;
    if (self.viewController.navigationController) {
        NSArray *stackedVCs = self.viewController.navigationController.viewControllers;
        if (stackedVCs && stackedVCs.count > 1) {
            UIViewController *previouseVC = [stackedVCs firstObject];
            if ([previouseVC isKindOfClass:[EUPageJumpViewController class]]) {
                targetVC = (EUPageJumpViewController *)previouseVC;
            }
        }
    }
    return targetVC;
}

- (EUPageJumpViewController *)getPageWithPageIDInPushStack:(NSString *)targetPageID {
    EUPageJumpViewController *targetVC = nil;
    if (self.viewController.navigationController) {
        NSArray *stackedVCs = self.viewController.navigationController.viewControllers;
        NSInteger currentPageIndex = [self currentPageIndex];
        if (stackedVCs && stackedVCs.count > 1 && currentPageIndex >= 1) {
            for (NSInteger pageIndex = currentPageIndex - 1; pageIndex > 0; pageIndex--) {
                UIViewController *page = [stackedVCs objectAtIndex:pageIndex];
                if ([page isKindOfClass:[EUPageJumpViewController class]]) {
                    if ([[(EUPageJumpViewController *)page getCurrentPageID] isEqualToString:targetPageID]) {
                        targetVC = (EUPageJumpViewController *)page;
                        break;
                    }
                }
            }
        }
    }
    return targetVC;
}

//尝试获取command附带的dataDic，并加入发起动作的pageID
- (NSDictionary *)embedDataWithPageID:(CDVInvokedUrlCommand *)command dataIndex:(NSInteger)dataIndex {
    NSAssert(dataIndex >= 0, @"data index shouldn't less then zero");
    NSMutableDictionary *embedDataDic = [NSMutableDictionary dictionary];
    if (command && command.arguments && command.arguments.count >= (dataIndex + 1)) {
        if ([[command.arguments objectAtIndex:dataIndex] isKindOfClass:[NSDictionary class]]) {
            embedDataDic = [[command.arguments objectAtIndex:dataIndex] mutableCopy];
        }
    }
    [embedDataDic setValue:[self currentPageID] forKey:kControlFromPageIDKey];
    return embedDataDic;
}
@end
